package netty.dubbo.client.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import netty.dubbo.api.bean.InvokeMessage;
import netty.dubbo.client.discovery.DefaultServiceDiscovery;
import netty.dubbo.client.discovery.ServiceDiscovery;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author: 程序员七哥
 * @date: 2021-12-19
 * @description: 客户端创建一个动态代理类，用于代理服务端提供的服务对象
 */
public class RpcProxy {

    public static <T> T create(Class<?> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
                new Class[]{clazz}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 如果是 Object 声明的方法则不用进行 Rpc 调用
                        if (Object.class.equals(method.getDeclaringClass())) {
                            return method.invoke(this,args);
                        }
                        // rpc 远程调用
                        return rpcInvoke(clazz, method, args);
                    }
                });
    }

    private static Object rpcInvoke(Class<?> clazz, Method method, Object[] args) throws Exception {

        ServiceDiscovery discovery = new DefaultServiceDiscovery();
        // 根据服务名对应的服务提供方地址
        final String serverAddress = discovery.discovery(clazz.getName());
        if (serverAddress == null) {
            System.out.println("该服务暂未获取到服务提供方，请于开发人员联系！");
            return null;
        }

        // 客户端 Rpc 调用处理器
        RpcClientHandler clientHandler = new RpcClientHandler();

        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup)
                    // 不使用缓冲算法，等数据量达到一定容器在发送，而是直接发送
                    .option(ChannelOption.TCP_NODELAY,true)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new ObjectEncoder());
                            pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                            pipeline.addLast(clientHandler);
                        }
                    });
            final String ip = serverAddress.split(":")[0];
            final int port = Integer.parseInt(serverAddress.split(":")[1]);
            final ChannelFuture future = bootstrap.connect(ip, port).sync();

            // 组装调用信息发送给 Netty 服务端
            InvokeMessage invokeMessage = new InvokeMessage();
            invokeMessage.setClassName(clazz.getName());
            invokeMessage.setMethodName(method.getName());
            invokeMessage.setParameterTypes(method.getParameterTypes());
            invokeMessage.setParameterValues(args);
            future.channel().writeAndFlush(invokeMessage).sync();

            future.channel().closeFuture().sync();
        } finally {
            eventLoopGroup.shutdownGracefully();
        }

        return clientHandler.getResult();

    }
}
